﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace ZBJF.Warranty.Common.Common
{
    public static class SecuritySignHelper
    {
        public const string Key = "key";
        public const string Sign = "sign";

        public const string VendorID = "VendorID";
        public const string Version = "Version";
        public const string Channel = "Channel";
        public const string TimeStamp = "TimeStamp";
        /// <summary>
        /// 获取防篡改签名，组织原始字符串的方式为:先get后post，该签名要求key在加密时为全小写，同时该方法隐含要求key和sign必须通过QueryString方式传递
        /// </summary>
        /// <param name="getCollection">通过QueryString方式传递的键值集合,如果内部包含key或者sign，相关字段在组织原始字符串时将会被移除</param>
        /// <param name="keyValue">合作账号</param>
        /// <param name="secrectKey">secrectKey</param>
        /// <param name="postCollection">通过Form方式传递的键值集合，如果包含key或者sign，此部分不会被做特殊处理</param>
        /// <returns></returns>
        public static string GetSecuritySign(this NameValueCollection getCollection, string keyValue, string secrectKey, NameValueCollection postCollection = null)
        {
            if (string.IsNullOrWhiteSpace(keyValue) || string.IsNullOrWhiteSpace(secrectKey))
            {
                throw new ArgumentNullException();
            }
            var dic = SecuritySignHelper.GetSortedDictionary(getCollection,
                (k) =>
                {   //过滤key及sign
                    return
                        string.Equals(k, SecuritySignHelper.Key, StringComparison.OrdinalIgnoreCase)
                        ||
                        string.Equals(k, SecuritySignHelper.Sign, StringComparison.OrdinalIgnoreCase);
                });
            dic.Add(SecuritySignHelper.Key, keyValue);

            StringBuilder tmp = new StringBuilder();
            SecuritySignHelper.FillStringBuilder(tmp, dic);//将QueryString填入StringBuilder
            dic = SecuritySignHelper.GetSortedDictionary(postCollection);

            SecuritySignHelper.FillStringBuilder(tmp, dic);//将Form填入StringBuilder
            tmp.Append(secrectKey);//在尾部添加secrectKey
            tmp.Remove(0, 1);//移除第一个&
            return tmp.ToString().GetMD5_32();//获取32位长度的Md5摘要
        }
        private static SortedDictionary<string, string> GetSortedDictionary(NameValueCollection collection, Func<string, bool> filter = null)
        {
            //获取排序的键值对
            SortedDictionary<string, string> dic = new SortedDictionary<string, string>();
            if (collection != null && collection.Count > 0)
            {
                foreach (var k in collection.AllKeys)
                {
                    if (filter == null || !filter(k))
                    {//如果没设置过滤条件或者无需过滤
                        dic.Add(k, collection[k]);
                    }
                }
            }
            return dic;
        }
        private static void FillStringBuilder(StringBuilder builder, SortedDictionary<string, string> dic)
        {
            foreach (var kv in dic)
            {
                builder.Append('&');
                builder.Append(kv.Key);
                builder.Append('=');
                builder.Append(kv.Value);
            }//按key顺序组织字符串
        }

        #region 加密 解密 注释
        //public static string GetMD5(string myString)
        //{
        //    string ret = null;
        //    MD5 md5 = MD5.Create();//实例化一个md5对像
        //    // 加密后是一个字节类型的数组，这里要注意编码UTF8/Unicode等的选择　
        //    byte[] s = md5.ComputeHash(Encoding.UTF8.GetBytes(myString));
        //    // 通过使用循环，将字节类型的数组转换为字符串，此字符串是常规字符格式化所得
        //    for (int i = 0; i < s.Length; i++)
        //    {
        //        // 将得到的字符串使用十六进制类型格式。格式后的字符是小写的字母，如果使用大写（X）则格式后的字符是大写字符 

        //        ret += s[i].ToString("x");

        //    }
        //    return ret;
        //}
        //public static string DESEncrypt(string text, string sKey)
        //{
        //    DESCryptoServiceProvider des = new DESCryptoServiceProvider();
        //    des.Mode = CipherMode.ECB;//兼容其他语言的Des加密算法 
        //    des.Padding = PaddingMode.PKCS7;
        //    byte[] inputByteArray = Encoding.UTF8.GetBytes(text);
        //    des.Key = ASCIIEncoding.UTF8.GetBytes(sKey);
        //    des.IV = ASCIIEncoding.UTF8.GetBytes(sKey);
        //    MemoryStream ms = new MemoryStream();
        //    CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write);
        //    cs.Write(inputByteArray, 0, inputByteArray.Length);
        //    cs.FlushFinalBlock();
        //    StringBuilder ret = new StringBuilder();
        //    foreach (byte b in ms.ToArray())
        //    {
        //        ret.AppendFormat("{0:X2}", b);
        //    }

        //    return ret.ToString();


        //    //return Convert.ToBase64String(MStream.ToArray());//返回加密后的字符串  
        //}

        //public static string DESDecrypt(string dataToDecrypt, string sKey)
        //{
        //    using (DESCryptoServiceProvider des = new DESCryptoServiceProvider())
        //    {
        //        try
        //        {
        //            byte[] inputByteArray = new byte[dataToDecrypt.Length / 2];
        //            for (int x = 0; x < dataToDecrypt.Length / 2; x++)
        //            {
        //                int i = (Convert.ToInt32(dataToDecrypt.Substring(x * 2, 2), 16));
        //                inputByteArray[x] = (byte)i;
        //            }
        //            des.Mode = CipherMode.ECB; //兼容其他语言的Des加密算法 
        //            des.Padding = PaddingMode.PKCS7;
        //            des.Key = ASCIIEncoding.ASCII.GetBytes(sKey); //建立加密对象的密钥和偏移量，此值重要，不能修改
        //            des.IV = ASCIIEncoding.ASCII.GetBytes(sKey);
        //            using (MemoryStream ms = new MemoryStream())
        //            {
        //                using (CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Write))
        //                {
        //                    cs.Write(inputByteArray, 0, inputByteArray.Length);
        //                    cs.FlushFinalBlock();
        //                    return System.Text.Encoding.UTF8.GetString(ms.ToArray());
        //                }
        //            }
        //        }
        //        catch (Exception ex)
        //        {
        //            return string.Empty;
        //        }
        //    }
        //}

        #endregion


        public static string DESEncrypt(string text, string sKey)
        {
            //DESCryptoServiceProvider des = new DESCryptoServiceProvider();
            //des.Mode = CipherMode.ECB;//兼容其他语言的Des加密算法 
            //des.Padding = PaddingMode.PKCS7;
            //byte[] inputByteArray = Encoding.UTF8.GetBytes(text);
            //des.Key = ASCIIEncoding.UTF8.GetBytes(sKey);
            //des.IV = ASCIIEncoding.UTF8.GetBytes(sKey);
            //MemoryStream ms = new MemoryStream();
            //CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write);
            //cs.Write(inputByteArray, 0, inputByteArray.Length);
            //cs.FlushFinalBlock();
            //StringBuilder ret = new StringBuilder();
            //foreach (byte b in ms.ToArray())
            //{
            //    ret.AppendFormat("{0:X2}", b);
            //}

            //return ret.ToString();
            byte[] btKey = Encoding.UTF8.GetBytes(sKey);
            byte[] btIV = Encoding.UTF8.GetBytes(sKey);
            DESCryptoServiceProvider des = new DESCryptoServiceProvider();
            using (MemoryStream ms = new MemoryStream())
            {
                byte[] inData = Encoding.UTF8.GetBytes(text);
                try
                {
                    using (CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(btKey, btIV), CryptoStreamMode.Write))
                    {
                        cs.Write(inData, 0, inData.Length);
                        cs.FlushFinalBlock();
                    }

                    return Convert.ToBase64String(ms.ToArray());
                }
                catch
                {
                    throw;
                }
            }


            //return Convert.ToBase64String(MStream.ToArray());//返回加密后的字符串  
        }

        public static string DESDecrypt(string dataToDecrypt, string sKey)
        {
            //using (DESCryptoServiceProvider des = new DESCryptoServiceProvider())
            //{
            //    try
            //    {
            //        byte[] inputByteArray = new byte[dataToDecrypt.Length / 2];
            //        for (int x = 0; x < dataToDecrypt.Length / 2; x++)
            //        {
            //            int i = (Convert.ToInt32(dataToDecrypt.Substring(x * 2, 2), 16));
            //            inputByteArray[x] = (byte)i;
            //        }
            //        des.Mode = CipherMode.ECB; //兼容其他语言的Des加密算法 
            //        des.Padding = PaddingMode.PKCS7;
            //        des.Key = ASCIIEncoding.ASCII.GetBytes(sKey); //建立加密对象的密钥和偏移量，此值重要，不能修改
            //        des.IV = ASCIIEncoding.ASCII.GetBytes(sKey);
            //        using (MemoryStream ms = new MemoryStream())
            //        {
            //            using (CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Write))
            //            {
            //                cs.Write(inputByteArray, 0, inputByteArray.Length);
            //                cs.FlushFinalBlock();
            //                return System.Text.Encoding.UTF8.GetString(ms.ToArray());
            //            }
            //        }
            //    }
            //    catch (Exception ex)
            //    {
            //        CommonMethod.WriteLogs(ex.ToString());
            //        return string.Empty;
            //    }
            //}
            //转义特殊字符
            dataToDecrypt = dataToDecrypt.Replace("-", "+");
            dataToDecrypt = dataToDecrypt.Replace("_", "/");
            dataToDecrypt = dataToDecrypt.Replace("~", "=");
            byte[] inputByteArray = Convert.FromBase64String(dataToDecrypt);
            using (DESCryptoServiceProvider des = new DESCryptoServiceProvider())
            {
                des.Key = ASCIIEncoding.ASCII.GetBytes(sKey);
                des.IV = ASCIIEncoding.ASCII.GetBytes(sKey);
                System.IO.MemoryStream ms = new System.IO.MemoryStream();
                try
                {
                    using (CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Write))
                    {

                        cs.Write(inputByteArray, 0, inputByteArray.Length);
                        cs.FlushFinalBlock();
                        cs.Close();
                    }
                }
                catch (Exception)
                {

                }
                string str = Encoding.UTF8.GetString(ms.ToArray());
                ms.Close();
                return str;
            }
        }
        /// <summary>
        /// 获取32位长度的Md5摘要
        /// </summary>
        /// <param name="inputStr"></param>
        /// <param name="encoding"></param>
        /// <returns></returns>
        public static string GetMD5_32(this string inputStr, Encoding encoding = null)
        {
            RefEncoding(ref encoding);
            byte[] data = GetMD5(inputStr, encoding);
            StringBuilder tmp = new StringBuilder();
            for (int i = 0; i < data.Length; i++)
            {
                tmp.Append(data[i].ToString("x2"));
            }
            return tmp.ToString();
        }
        private static byte[] GetMD5(string inputStr, Encoding encoding)
        {
            using (MD5 md5Hash = MD5.Create())
            {
                return md5Hash.ComputeHash(encoding.GetBytes(inputStr));
            }
        }
        private static void RefEncoding(ref Encoding encoding)
        {
            if (encoding == null)
            {
                encoding = Encoding.Default;
            }
        }
    }
}
